/**
 * @fileoverview
 * This script defines utilities for interacting with data from www.fly-ted.org.
 * @author <a href="http://purl.org/net/aliman">Alistair Miles</a>, Graham Klyne, Jun Zhao
 * @version $Revision:538 $ on $Date: 2008-08-27 09:08:41 +0100 (Wed, 27 Aug 2008) $ by $Author: aliman $
 * @requires YAHOO.util.Connect
 * @requires flyui.flyted.GeneNameMappings
 * TODO license terms
 */

// create a namespace if not already defined
flyui.namespace("flyui.flyted");


/**
 * TODO doc me
 */
flyui.flyted.Service = function (endpointURL) {
	/**
	 * @private
	 */
	this._endpointURL = endpointURL;
}


/**
 * TODO doc me
 */
flyui.flyted.Service.prototype.query = function (query, success, failure){
	log("flyui.flyted.Service :: query request: "+query);
	
	// define the callback object
	var callback = {
		success: success,
		failure: failure
	};
			
	var url = this._buildURL(query);
	
	log("flyui.flyted.Service :: make the request to "+url);
	var request = YAHOO.util.Connect.asyncRequest("GET", url, callback, null);
	
};


/**
 * TODO doc me
 */
flyui.flyted.Service.prototype.findImagesByFlytedProbeName = function( probeName, success, failure ) {
	log("flyui.flyted.Service :: findImagesByFlytedProbeName: "+probeName);
	
	var successAdapter = flyui.flyted.Service._buildSuccessAdapterForFindImagesByFlytedProbeName(success);

	var query = flyui.flyted.Service._buildQueryForFindImagesByFlytedProbeName(probeName);
	
	this.query(query, successAdapter, failure);
	
};


/**
 * Find images in the FlyTED database for the given flybase gene ID.
 * @param {String} flybaseGeneID the flybase gene ID e.g. FBgn012345
 * @param {Function} success the success case callback function, which must accept an array of 
 * flyui.flyted.Image objects
 * @param {Function} failure the failure case callback function, which must accept a YUI HTTP response object
 */
flyui.flyted.Service.prototype.findImagesByFlybaseGeneID = function( flybaseGeneID, success, failure) {
	log("flyui.flyted.Service :: findImagesByFlybaseGeneID: "+flybaseGeneID);
	
	var successAdapter = flyui.flyted.Service._buildSuccessAdapterForFindImagesByFlybaseGeneID(success);

	var query = flyui.flyted.Service._buildQueryForFindImagesByFlybaseGeneID(flybaseGeneID);
	
	this.query(query, successAdapter, failure);
	
}

/**
 * Return a success case request handler for the findImagesByFlytedProbeName method.
 * @param {Function} adaptedSuccess the function to call with an array of Image objects
 * @return a success callback to use in the HTTP request
 * @type Function
 * @private
 * @static
 */
flyui.flyted.Service._buildSuccessAdapterForFindImagesByFlybaseGeneID = function( adaptedSuccess ) {

	return function(responseObject) {
		
		log("response received: "+responseObject.status, "debug");
		
		// try parsing response text as json
		log("parsing response: "+responseObject.responseText, "debug");
		var resultSet = YAHOO.lang.JSON.parse(responseObject.responseText);
		
		// convert result set to an array of genes
		var images = flyui.flyted.Image.newInstancesFromSPARQLResults(resultSet);
		
		// complete the request
		adaptedSuccess(images);
	}

};


/**
 * Return a success case request handler for the findImagesByFlytedProbeName method.
 * @param {Function} adaptedSuccess the function to call with an array of Image objects
 * @return a success callback to use in the HTTP request
 * @type Function
 * @private
 * @static
 */
flyui.flyted.Service._buildSuccessAdapterForFindImagesByFlytedProbeName = function( adaptedSuccess ) {

	return function(responseObject) {
		
		log("response received: "+responseObject.status, "debug");
		
		// try parsing response text as json
		log("parsing response: "+responseObject.responseText, "debug");
		var resultSet = YAHOO.lang.JSON.parse(responseObject.responseText);
		
		// convert result set to an array of genes
		var images = flyui.flyted.Image.newInstancesFromSPARQLResults(resultSet);
		
		// complete the request
		adaptedSuccess(images);
	}

};


/**
 * Build a SPARQL query to retrieve images by flyted probe name.
 * @param {String} anyName the probe name to search by
 * @return a SPARQL query
 * @type String
 * @private
 * @static
 */
flyui.flyted.Service._buildQueryForFindImagesByFlytedProbeName = function( probeName ) {

	var prefixes = 	"PREFIX flyted: <http://purl.org/net/flyted/schema/> " +
					"PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> " +
					"PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> ";
					
	var body = 		"SELECT DISTINCT *  WHERE { " +
						"?fullImageURL " +
							"flyted:probe ?probe ; " +
							//"flyted:associatesToGene ?gene ; " +
							"flyted:thumbnail ?thumbnailURL; " +
							"rdfs:seeAlso ?flytedURL; " +
							"rdfs:label ?caption ." +
						"?probe rdfs:label '" + probeName + "' ." +
					"}";
						
	var query = prefixes + body;

	return query;
};

flyui.flyted.Service._buildQueryForFindImagesByFlybaseGeneID = function( flybaseGeneID ) {
	var prefixes = 	"PREFIX flyted: <http://purl.org/net/flyted/schema/> " +
					"PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> " +
					"PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> ";
					"PREFIX so: <http://purl.org/obo/owl/SO#> ";
					
	var body = 		"SELECT DISTINCT ?fullImageURL ?thumbnailURL ?flytedURL ?caption ?probe ?probeLabel WHERE { " +
						"?probe flyted:hybridisesTranscriptOf <http://openflydata.org/id/flybase/feature/" + flybaseGeneID + "> ;" +
							"rdfs:label ?probeLabel ." +
						"?fullImageURL " +
							"flyted:probe ?probe ; " +
							"flyted:thumbnail ?thumbnailURL; " +
							"rdfs:seeAlso ?flytedURL; " +
							"rdfs:label ?caption ." +
					"}";
						
	var query = prefixes + body;

	return query;
	
};


/**
 * Build a SPARQL query to retrieve images by a union of multiple flyted probe names.
 * @param {[String]} anyNames the array of probe names to search by
 * @return a SPARQL query
 * @type String
 * @private
 * @static
 */
//flyui.flyted.Service._buildQueryForFindImagesByUnionOfFlytedProbeNames = function( probeNames ) {
//
//	var prefixes = 	"PREFIX flyted: <http://purl.org/net/flyted/schema/> " +
//					"PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> " +
//					"PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> ";
//		
//	if (probeNames.length == 0) {
//		// this should never happen	
//		throw {name:"flyui.flyted.Service.InvalidQueryException", message:"cannot query for union of empty array of probe names"};
//	}				
//	else if (probeNames.length == 1) {
//		return flyui.flyted.Service._buildQueryForFindImagesByFlytedProbeName(probeNames[0]);
//	}
//	else {
//		
//		var pattern = function( probeName ) {
//			return 	"{ "+
//						"?fullImageURL " +
//							"flyted:probe ?probe ; " +
//							//"flyted:associatesToGene ?gene ; " +
//							"flyted:thumbnail ?thumbnailURL; " +
//							"rdfs:seeAlso ?flytedURL; " +
//							"rdfs:label ?caption ." +
//						"?probe rdfs:label '" + probeName + "' ." +
//					"} ";
//		}
//		
//		var body = 		"SELECT DISTINCT * WHERE { ";
//		body +=			pattern(probeNames[0]);
//		for (var i=1; i<probeNames.length; i++) {
//			body += 	"UNION ";
//			body +=		pattern(probeNames[i]);
//		}
//		body += 		"}";
//		
//	}
//
//	var query = prefixes + body;
//
//	return query;
//}


/**
 * Construct the request URL for the given SPARQL query.
 * @private
 * @param {String} query a SPARQL query
 * @return a request URL
 * @type String
 */
flyui.flyted.Service.prototype._buildURL = function( query ) {
	return this._endpointURL + "?output=json&query=" + escape(query);
};


/**
 * Map a string to a list of strings via a supplied mapping table,
 * returnimng an empty list if the supplied key is not present. 
 * @param {String} key 		a key value to be mapped
 * @param {Object} table 	an object with members that are arrays of strings
 * @return an array of string values, which may be empty if the given key is not found
 * @type [String]
 */
flyui.flyted.Service.mapNameUsingTable = function ( key, table ) {
	var result = table[key];
	if (typeof result == "undefined" || !result ) {
		result = [];
	}
	return result;
}


/**
 * Map a Flybase ID to zero, one or more FlyTED gene names.  Note that if there is no known mapping
 * for the supplied gene name, an empty (zero-length) array is returned.
 * @param {String} a Flybase ID
 * @return an array of FlyTED gene names that return data corresponding to the given Flybase ID
 * @type [String]
 */
//flyui.flyted.Service.mapFlybaseIDToFlytedGeneName = function( flybaseID ) {
//	
//	return flyui.flyted.Service.mapNameUsingTable(flybaseID, flyui.flyted.GeneNameMappings.flybaseToFlyted);
//	
//}
//
//
///**
// * Map a Flybase ID to zero, one or more FlyTED gene names.  Note that if there is no known mapping
// * for the supplied Flybase ID, an empty (zero-length) array is returned.
// * @param {String} a Flybase ID
// * @return an array of FlyTED gene names that return data corresponding to the given Flybase ID
// * @type [String]
// */
//flyui.flyted.Service.mapFlytedGeneNameToFlybaseID = function( flytedName ) {
//
//	return flyui.flyted.Service.mapNameUsingTable(flytedName, flyui.flyted.GeneNameMappings.flytedToFlybase);
//
//}


/**
 * TDO doc me
 */
flyui.flyted.Image = function () {
	
	/**
	 * @type String
	 */
	this.fullImageURL = null;
	
	/**
	 * @type String
	 */
	this.thumbnailURL = null;
	
	/**
	 * @type String
	 */
	 
	this.flytedURL = null;
	
	/**
	 * @type String
	 */
	this.caption = null;
	
	/**
	 * @type String
	 */
	this.probe = null;
	
//	this.addProbe = function (probe){
//		flyui.util.appendIfNotMember(that.images, image);
//	};
	
}


/**
 * TODO doc me
 */
flyui.flyted.Image.newInstancesFromSPARQLResults = function(resultSet){
	var bindings = resultSet.results.bindings;
	var imagePool = new flyui.flyted.ImagePool();
	var probePool = new flyui.flyted.ProbePool();
	
	for (var i=0; i<bindings.length; i++) {
		var binding = bindings[i];
		
		var imageURI = binding.fullImageURL.value;
		//TODO
		var image = imagePool.get(imageURI);
		image.fullImageURL = binding.fullImageURL.value;
		image.thumbnailURL = binding.thumbnailURL.value;
		image.flytedURL = binding.flytedURL.value;
		image.caption = binding.caption.value;
		
		if (binding.probe){
			var probeURI = binding.probe.value;
			var probe = probePool.get(probeURI);
			
			if (!probe.probeLabels){
				probe.probeLabels = new Array();
			}

			if (binding.probeLabel) {
				var probeLabel = binding.probeLabel.value;
				flyui.util.appendIfNotMember(probe.probeLabels, probeLabel);
			}
			//image.addProbe(probe);
			image.probe = probe;
		}		
	}
	
	return imagePool.toArray();
}

flyui.flyted.ImagePool = function() {
	/**
	 * @private
	 */
	this._pool = new Object();
};

flyui.flyted.ImagePool.prototype.toArray = function() {
	var array = new Array();
	for (var key in this._pool) {
		array[array.length] = this._pool[key];
	}
	return array;
}

flyui.flyted.ImagePool.prototype.get = function( imagepath ) {
	
	var image = this._pool[imagepath];
	
	if ( typeof image == "undefined" || !image ) {
		image = new flyui.flyted.Image();		
		this._pool[imagepath] = image;	
	}
	
	return image;

};

flyui.flyted.ProbePool = function() {
	/**
	 * @private
	 */
	this._pool = new Object();
};

flyui.flyted.ProbePool.prototype.get = function( probeURI ) {
	
	var probe = this._pool[probeURI];
	
	if (!probe ) {
		probe = new Object();
		probe.probeURI = probeURI;
		this._pool[probeURI] = probe;	
	}
	
	return probe;

};

flyui.flyted.ProbePool.prototype.toArray = function() {
	var array = new Array();
	for (var key in this._pool) {
		array[array.length] = this._pool[key];
	}
	return array;
}